'''
Created on 8. nov. 2010

@author: kukovec
'''

import wx
import math
import cStringIO
import urllib2
import os

class CGoogleMapsBackend():
    def __init__(self, aRefreshCallback):
        # Callback that will be called when data changes
        self.refreshCallback = aRefreshCallback
        # Viewer size in pixels
        self.viewerSize = [0, 0]
        # Position in longitude and latitude degrees as viewer sees it; (0, 0) map center
        self.mapPosition = [46.419989, 15.869981]
        #self.mapPosition = [0, 0]
        self.zoomLevel = 2
        self.minZoomLevel = 0
        self.maxZoomLevel = 19
        self.zoomLevelStep = 1
        self.pixelsPerTileSide = 256
        # Actual map bitmap
        self.mapBitmap = None
        # Create database path
        self.databasePath = 'goggle.maps.database'
        if True == os.path.exists(self.databasePath) and False == os.path.isdir(self.databasePath):
            os.remove(self.databasePath)
        if False == os.path.exists(self.databasePath):
            os.mkdir(self.databasePath)      
    def calculateParams(self):
        print ''
        print 'Calculate params'
        # Position in longitude and latitude as backend sees it; (0, 0) upper left corner
        self.backendMapPosition = [180 + self.mapPosition[0], 90 - self.mapPosition[1]]
        # Tiles per map side according to zoom level
        self.tilesPerMapSide = int(math.sqrt(math.pow(4, self.zoomLevel)))
        # Tile on which mapPosition resides according to zoom level
        self.mapPositionTile = [self.backendMapPosition[0] * self.tilesPerMapSide / 360, self.backendMapPosition[1] * self.tilesPerMapSide / 180]
        #self.mapPositionTile = [int(self.backendMapPosition[0] * self.tilesPerMapSide / 360), int(self.backendMapPosition[1] * self.tilesPerMapSide / 180)]
        # Degrees per tile
        self.degreesPerTile = [360.0 / self.tilesPerMapSide, 180.0 / self.tilesPerMapSide]
        # Position offset in pixels on the mapPositionTile
        self.mapPositionTilePixOffset = [(self.backendMapPosition[0] % self.degreesPerTile[0] * self.pixelsPerTileSide) / self.degreesPerTile[0], (self.backendMapPosition[1] % self.degreesPerTile[1] * self.pixelsPerTileSide) / self.degreesPerTile[1]]
        # Viewer region focus point in pixels by default in the viewer region center
        self.viewerRegionFocusPoint = [self.viewerSize[0] / 2, self.viewerSize[1] / 2]
        # Calculate tile region for the viewer region size; structure [[x0, y0],[xn, yn]]
        self.viewerTileRegion = self.calculateTileRegion(self.viewerRegionFocusPoint)
        # Calculate how many tiles is needed per each viewerTileRegion
        self.viewerRegionTilesPerSide = [self.viewerTileRegion[1][0] + 1 - self.viewerTileRegion[0][0], self.viewerTileRegion[1][1] + 1 - self.viewerTileRegion[0][1]] 
        # Test printout        
        print '-----------------------------------'
        print 'map position: ' + str(self.mapPosition)
        print 'backend map position: ' + str(self.backendMapPosition)
        print 'degrees per tile: ' + str(self.degreesPerTile)
        print 'position tile pixel offset: ' + str(self.mapPositionTilePixOffset)
        print 'map size: [%d, %d]' % (self.tilesPerMapSide * self.pixelsPerTileSide, self.tilesPerMapSide * self.pixelsPerTileSide)
        print 'viewer region size: ' + str(self.viewerSize)
        print '-----------------------------------'
        print 'position tile: ' + str(self.mapPositionTile)
        print 'tile region: ' + str(self.viewerTileRegion)
        #print 'region tiles per side [%d, %d]' % (self.viewerRegionTilesPerSide[0], self.viewerRegionTilesPerSide[1])
    def calculateTileRegion(self, aVieverRegionFocusPoint):
        # Calculate tile region to fill up viewers region
        tileXstart = self.mapPositionTile[0] - int(math.ceil((aVieverRegionFocusPoint[0] - self.mapPositionTilePixOffset[0]) * 1.0/ self.pixelsPerTileSide))
        tileYstart = self.mapPositionTile[1] - int(math.ceil((aVieverRegionFocusPoint[1] - self.mapPositionTilePixOffset[1]) * 1.0/ self.pixelsPerTileSide))
        tileXend = self.mapPositionTile[0] + int(math.ceil((self.viewerSize[0] - aVieverRegionFocusPoint[0] - self.pixelsPerTileSide + self.mapPositionTilePixOffset[0]) * 1.0 / self.pixelsPerTileSide))
        tileYend = self.mapPositionTile[1] + int(math.ceil((self.viewerSize[1] - aVieverRegionFocusPoint[1] - self.pixelsPerTileSide + self.mapPositionTilePixOffset[1]) * 1.0 / self.pixelsPerTileSide))
        return [[tileXstart, tileYstart], [tileXend, tileYend]]
    def getZoomLevel(self):
        return self.zoomLevel;
    def getMaxZoomLevel(self):
        return self.maxZoomLevel
    def getMinZoomLevel(self):
        return self.minZoomLevel
    def getZoomLevelStep(self):
        return self.zoomLevelStep
    def increaseZoomLevel(self):
        self.zoomLevel = self.zoomLevel + self.zoomLevelStep
        if self.zoomLevel > self.maxZoomLevel:
            self.zoomLevel = self.maxZoomLevel
    def decreaseZoomLevel(self):
        self.zoomLevel = self.zoomLevel - self.zoomLevelStep
        if self.zoomLevel < self.minZoomLevel:
            self.zoomLevel = self.minZoomLevel
    def moveInPixels(self, aX, aY):
        pass
    def getData(self, aViewerSize):
        self.viewerSize = aViewerSize
        self.calculateParams()
        # Create bitmap
        self.mapBitmap = wx.EmptyBitmap(self.pixelsPerTileSide * self.viewerRegionTilesPerSide[0], self.pixelsPerTileSide * self.viewerRegionTilesPerSide[1])
        dc = wx.MemoryDC();
        dc.SelectObject(self.mapBitmap)
        xAxisTile = 0;
        yAxisTile = 0;
        for xAxis in range(self.viewerTileRegion[0][0], self.viewerTileRegion[1][0] + 1):
            for yAxis in range(self.viewerTileRegion[0][1], self.viewerTileRegion[1][1] + 1):
                if xAxis >= 0 and xAxis < self.tilesPerMapSide and yAxis >= 0 and yAxis < self.tilesPerMapSide:
                    bitmapFetched = False
                    bitmapPath = self.databasePath + "/tile.Z" + str(self.zoomLevel) + ".X" + str(xAxis) + ".Y" + str(yAxis) + ".png"
                    bitmap = wx.EmptyBitmap(self.pixelsPerTileSide, self.pixelsPerTileSide)
                    try:
                        bitmapFile = open(bitmapPath, 'rb')
                        bitmap = wx.BitmapFromImage(wx.ImageFromStream(cStringIO.StringIO(bitmapFile.read())))
                        bitmapFetched = True
                    except IOError:
                        # File does not exist, fetch data
                        bitmapURL = "http://mt0.google.com/vt/lyrs=m@135&hl=en&x=" + str(xAxis) + "&y=" + str(yAxis) + "&z=" + str(self.zoomLevel) + "&s=Ga"
                        try:                        
                            urlBitmapFile = urllib2.urlopen(bitmapURL);
                            # Creating bitmap file
                            image = wx.ImageFromStream(cStringIO.StringIO(urlBitmapFile.read()))
                            if image.IsOk():
                                bitmap = wx.BitmapFromImage(image)
                                bitmapFetched = True                        
                        except urllib2.URLError:
                            print 'URL error!'
                        except urllib2.HTTPError:
                            print 'URL error!'
                    if bitmapFetched:
                        bitmap.SaveFile(bitmapPath, wx.BITMAP_TYPE_PNG)
                        dc.DrawBitmap(bitmap, xAxisTile * self.pixelsPerTileSide, yAxisTile * self.pixelsPerTileSide, False)
                yAxisTile = yAxisTile + 1
            xAxisTile = xAxisTile + 1
            yAxisTile = 0
        dc.SelectObject(wx.NullBitmap)
        self.refreshCallback(self.mapBitmap, [self.mapBitmap.GetWidth() / 2, self.mapBitmap.GetHeight() / 2])
